﻿using System.Diagnostics;

namespace DimensionsHelper.LanguageService.Syntax;

// [.] use define_list
// [sublist[rot[ate] | ran[domize] | rev[erse] |
//     asc[ending] | desc[ending]]]
public class MetadataListReference : MetadataLabeledObject, IMetadataOrder
{
    private SyntaxToken? _fixKeywordToken;
    private SyntaxToken? _namespaceKeywordToken;
    private SyntaxList<SyntaxToken>? _orderTokens;
    private MetadataSubList? _subList;
    private SyntaxToken? _topLevelOperatorToken;

    internal MetadataListReference(SyntaxToken? dotToken, SyntaxToken useKeywordToken,
        Identifier listIdentifier, SyntaxNode? parent) :
        base(SyntaxKinds.MetadataListReference, useKeywordToken, listIdentifier, parent)
    {
        DotToken = dotToken;
    }


    /// <summary>
    ///     在最前面的'.'，可以省略。
    /// </summary>
    /// <remarks>
    ///     在mdd文档中，一般情况下的列表引用在字段的categories节点下使用categories表示，
    ///     除此之外，也同样允许在字段的categories上添加引用，此时，在序列化的脚本中体现为在
    ///     use关键字前添加一个'.'符号。此时，所在列表中只允许有一个对象，即当前列表引用。
    ///     此符号和TopLevel符号不冲突，后者添加在引用列表名前。
    /// </remarks>
    public SyntaxToken? DotToken { get; }


    /// <summary>
    ///     接收<see cref="SyntaxKinds.BackSlashBackSlashDotToken" />类型的Token。
    /// </summary>
    public SyntaxToken? TopLevelOperatorToken => _topLevelOperatorToken;


    /// <summary>
    ///     sublist 关键字
    /// </summary>
    public MetadataSubList? SubList => _subList;

    /// <summary>
    ///     排序关键字集合
    ///     [ rot[ate] | ran[domize] | rev[erse] | asc[ending] | desc[ending] ] ]
    /// </summary>
    public IReadOnlyList<SyntaxToken> OrderTokens =>
        _orderTokens == null ? new List<SyntaxToken>() : _orderTokens;


    /// <summary>
    ///     namespace 关键字
    /// </summary>
    public SyntaxToken? NamespaceKeywordToken => _namespaceKeywordToken;


    /// <summary>
    ///     fix 关键字
    /// </summary>
    public SyntaxToken? FixKeywordToken => _fixKeywordToken;


    /// <summary>
    ///     添加一个排序关键字
    /// </summary>
    public void AddOrder(SyntaxToken token)
    {
        Debug.Assert(token.IsOneOfNode(
            SyntaxKinds.RotKeywordToken, // Rot
            SyntaxKinds.RotateKeywordToken, // Rotate
            SyntaxKinds.RanKeywordToken, // Ran
            SyntaxKinds.RandomizeKeywordToken, // Randomize
            SyntaxKinds.RevKeywordToken, // Rev
            SyntaxKinds.ReverseKeywordToken, // Reverse
            SyntaxKinds.AscKeywordToken, // Asc
            SyntaxKinds.AscendingKeywordToken, // Ascending
            SyntaxKinds.DescKeywordToken, // Desc
            SyntaxKinds.DescendingKeywordToken // Descending
        ));
        AddChildMember(ref _orderTokens, token);
    }


    public void Fix(SyntaxToken fixKeywordToken)
    {
        Debug.Assert(fixKeywordToken.IsNode(SyntaxKinds.FixKeywordToken));
        SetChildNullableField(ref _fixKeywordToken, fixKeywordToken);
    }


    public void Update(SyntaxToken namespaceKeywordToken)
    {
        Debug.Assert(namespaceKeywordToken.IsNode(SyntaxKinds.NamespaceKeywordToken));
        SetChildNullableField(ref _namespaceKeywordToken, namespaceKeywordToken);
    }


    public MetadataListReference Update(SyntaxToken? topLevelToken, MetadataSubList? subList)
    {
        Debug.Assert(topLevelToken == null || topLevelToken.IsNode(SyntaxKinds.BackSlashBackSlashDotToken));
        SetChildNullableField(ref _topLevelOperatorToken, topLevelToken);
        SetChildNullableField(ref _subList, subList);
        return this;
    }

    public override void AcceptVisitor<T>(SyntaxNodeVisitor<T> visitor)
    {
        base.AcceptVisitor(visitor);
        AcceptList(visitor, OrderTokens);
        Accept(visitor, FixKeywordToken, NamespaceKeywordToken);
    }
}


public static partial class NodeFactory
{
    public static MetadataListReference CreateMetadataListReference(SyntaxNode? parent, SyntaxToken? dotToken,
        SyntaxToken useKeywordToken,
        Identifier listIdentifier, SyntaxToken? topLevelOperatorToken, MetadataSubList? subList)
    {
        Debug.Assert(useKeywordToken.IsNode(SyntaxKinds.UseKeywordToken));
        Debug.Assert(topLevelOperatorToken == null ||
                     topLevelOperatorToken.IsNode(SyntaxKinds.BackSlashBackSlashDotToken));
        MetadataListReference reference = new(dotToken, useKeywordToken, listIdentifier, parent);
        return reference.Update(topLevelOperatorToken, subList);
    }
}